////////////////////////////////////////////////////////////////////////////////
// $Id$
// Copyright (C) 2008
// Emory Merryman
// ree9opdz@gmail.com
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
////////////////////////////////////////////////////////////////////////////////

package com . linglingqi . factories ;

/**
 * This class uses another factory.
 * It lazily makes an object only if it is needed.
 * Then it caches that object.
 *
 * @author $Author$
 * @version $Revision$
 **/
public final class CachedFactories
{
    /**
     * Embedded Copyright Notice.
     **/
    public static final java . lang . String EMBEDDED_COPYRIGHT =
        "Copyright (C) 2008, Emory Merryman, ree9opdz@gmail.com" ;

    /**
     * Hide the no-args contructor.
     **/
    private CachedFactories ( )
	{
	    super ( ) ;
	}

    /**
     * Makes a Cache Factory out of the specified (not) cache factory.
     *
     * @param <R> the return type of the factory
     * @param <E> the type of exception to be thrown
     * @param rFactory the specified (not) cache factory
     * @return a cache factory
     **/
    public static final
	< R , E extends java . lang . Exception >
			Factory0 < R , E >
			instance
			( final Factory0 < R , E > rFactory )
    {
	final Factory0 < R , E > cacheFactory = new Factory0 < R , E > ( )
	    {
		private java . lang . ref . Reference < R > rRef ;

		private java . lang . ref . Reference < E > eRef ;

		public final R make ( ) throws E
		{
		    if ( this . rRef == null )
			{
			    if ( this . eRef == null )
				{
				    try
					{
					    final R r = rFactory . make ( ) ;
					    this . rRef =
						new java . lang . ref .
						SoftReference < R >
						( r ) ;
					}
				    catch
					( final java . lang . Exception cause )
					{
					    final E e = ( E ) ( cause ) ;
					    this . eRef =
						new
						java . lang . ref .
						SoftReference < E >
						( e ) ;
					    throw ( e ) ;
					}
				}
			}
		    final R make = this . rRef . get ( ) ;
		    return ( make ) ;
		}
	    } ;
	return ( cacheFactory ) ;
    }

    /**
     * Makes a Cache Factory out of the specified (not) cache factory.
     *
     * @param <R> the return type of the factory
     * @param <P1> the parameter type of the factory
     * @param <E> the type of exception to be thrown
     * @param rFactory the specified (not) cache factory
     * @return a cache factory
     **/
    public static final
	< R , P1 , E extends java . lang . Exception >
			     Factory1 < R , P1 , E >
			     instance
			     ( final Factory1 < R , P1 , E > rFactory )
    {
	final java . util . Map < P1 , R > rMap =
	    new java . util . Hashtable < P1 , R > ( ) ;
	final java . util . Map < P1 , E > eMap =
	    new java . util . Hashtable < P1 , E > ( ) ;
	final Factory1 < R , P1 , E > cacheFactory =
	    new Factory1 < R , P1 , E > ( )
	{
	    public final R make ( final P1 p1 ) throws E
	    {
		if ( rMap . get ( p1 ) == null )
		    {
			if ( eMap . get ( p1 ) == null )
			    {
				try
				    {
					final R r = rFactory . make ( p1 ) ;
					rMap . put ( p1 , r ) ;
				    }
				catch ( final java . lang . Exception cause )
				    {
					final E e = ( E ) ( cause ) ;
					eMap . put ( p1 , e ) ;
					throw ( e ) ;
				    }
			    }
		    }
		final R make = rMap . get ( p1 ) ;
		return ( make ) ;
	    }
	} ;
	return ( cacheFactory ) ;
    }
}
